/******************************************************************************
 Copyright (c) 2024 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

******************************************************************************/

#include "id3v2_parser_lib.h"
#include "id3v2_parser_private.h"
#include "id3v2_parser_debug.h"

static uint32 streamObjectHasBom(const uint8* streamObject, uint32 *cursor)
{
    if (streamObject == NULL)
    {
        return *cursor;
    }

    if (memcmp("\xFF\xFE", streamObject, 2) == 0 || memcmp("\xFE\xFF", streamObject, 2) == 0)
    {
        /* need to skip these 2 bytes */
        *cursor = *cursor+2;
    }
    return (*cursor);
}

static uint32 currentCursor(uint32* cursor, uint32 length)
{
    *cursor = *cursor + length;
    return (*cursor);
}

/*
 * Example data which is used to validate the utility
   char musicCanData[] = {
   0x49 ,0x44 ,0x33 ,0x03 ,0x00 ,0x00 ,0x00 ,0x00 ,0x03 ,0x00 ,0x54 ,0x58 ,0x58 ,0x58 ,0x00 ,0x00 ,0x00,
   0x1a ,0x00 ,0x00 ,0x00 ,0x45 ,0x6e ,0x63 ,0x6f ,0x64 ,0x65 ,0x64 ,0x20 ,0x62 ,0x79 ,0x00 ,0x61 ,0x62,
   0x63 ,0x53 ,0x6f ,0x6e ,0x67 ,0x73 ,0x4d ,0x70 ,0x33 ,0x2e ,0x43 ,0x6f ,0x54 ,0x58 ,0x58 ,0x58 ,0x00,
   0x00 ,0x00 ,0x22 ,0x00 ,0x00 ,0x00 ,0x4f ,0x72 ,0x69 ,0x67 ,0x69 ,0x6e ,0x61 ,0x6c ,0x20 ,0x61 ,0x72,
   0x74 ,0x69 ,0x73 ,0x74 ,0x28 ,0x73 ,0x29 ,0x00 ,0x61 ,0x62 ,0x63 ,0x53 ,0x6f ,0x6e ,0x67 ,0x73 ,0x4d,
   0x70 ,0x33 ,0x2e ,0x43 ,0x6f ,0x54 ,0x41 ,0x4c ,0x42 ,0x00 ,0x00 ,0x00 ,0x13 ,0x00 ,0x00 ,0x00 ,0x4f,
   0x54 ,0x53 ,0x20 ,0x69 ,0x6e ,0x20 ,0x4d ,0x43 ,0x50 ,0x34 ,0x20 ,0x28 ,0x32 ,0x30 ,0x32 ,0x34 ,0x29,
   0x54 ,0x49 ,0x54 ,0x32 ,0x00 ,0x00 ,0x00 ,0x1a ,0x00 ,0x00 ,0x00 ,0x54 ,0x72 ,0x61 ,0x63 ,0x6b ,0x34,
   0x20 ,0x3a ,0x3a ,0x20 ,0x61 ,0x62 ,0x63 ,0x53 ,0x6f ,0x6e ,0x67 ,0x73 ,0x4d ,0x70 ,0x33 ,0x2e ,0x43,
   0x6f ,0x6d ,0x54 ,0x52 ,0x43 ,0x4b ,0x00 ,0x00 ,0x00 ,0x02 ,0x00 ,0x00 ,0x00 ,0x34 ,0x43 ,0x4f ,
   0x4d ,0x4d ,0x00 ,0x00 ,0x00 ,0x13 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x61 ,0x62 ,0x63 ,0x53,
   0x6f ,0x6e ,0x67 ,0x73 ,0x4d ,0x70 ,0x33 ,0x2e ,0x43 ,0x6f ,0x43 ,0x4f ,0x4d ,0x4d ,0x00 ,0x00 ,0x00,
   0x13 ,0x00 ,0x00 ,0x00 ,0x58 ,0x58 ,0x58 ,0x00 ,0x61 ,0x62 ,0x63 ,0x53 ,0x6f ,0x6e ,0x67 ,0x73 ,0x4d,
   0x70 ,0x33 ,0x2e ,0x43 ,0x6f ,0x54 ,0x58 ,0x58 ,0x58 ,0x00 ,0x00 ,0x00 ,0x19 ,0x00 ,0x00 ,0x00 ,0x50,
   0x75 ,0x62 ,0x6c ,0x69 ,0x73 ,0x68 ,0x65 ,0x72 ,0x00 ,0x61 ,0x62 ,0x63 ,0x53 ,0x6f ,0x6e ,0x67 ,0x73,
   0x4d ,0x70 ,0x33 ,0x2e ,0x43 ,0x6f ,0x54 ,0x50 ,0x45 ,0x31 ,0x00 ,0x00 ,0x00 ,0x10 ,0x00 ,0x00 ,0x00,
   0x52 ,0x61 ,0x6a ,0x75 ,0x20 ,0x41 ,0x62 ,0x64 ,0x75 ,0x6c ,0x20 ,0x4a ,0x6f ,0x68 ,0x6e ,0x54 ,0x59,
   0x45 ,0x52 ,0x00 ,0x00 ,0x00 ,0x05 ,0x00 ,0x00 ,0x00 ,0x32 ,0x30 ,0x32 ,0x34 ,0x54 ,0x44 ,0x52 ,0x43,
   0x00 ,0x00 ,0x00 ,0x05 ,0x00 ,0x00 ,0x00 ,0x32 ,0x30 ,0x32 ,0x34 ,0x54 ,0x58 ,0x58 ,0x58 ,0x00 ,0x00,
   0x00 ,0x13 ,0x00 ,0x00 ,0x00 ,0x42 ,0x61 ,0x6e ,0x64 ,0x00 ,0x53 ,0x69 ,0x76 ,0x61 ,0x2c ,0x20 ,0x50,
   0x61 ,0x72 ,0x76 ,0x61 ,0x74 ,0x69 ,0x54 ,0x58 ,0x58 ,0x58 ,0x00 ,0x00 ,0x00 ,0x19 ,0x00 ,0x00 ,0x00,
   0x43 ,0x6f ,0x6d ,0x70 ,0x6f ,0x73 ,0x65 ,0x72 ,0x00 ,0x52 ,0x61 ,0x6a ,0x75 ,0x20 ,0x41 ,0x62 ,0x64,
   0x75 ,0x6c ,0x20 ,0x4a ,0x6f ,0x68 ,0x6e ,0x54 ,0x43 ,0x4f ,0x4e ,0x00 ,0x00 ,0x00 ,0x04 ,0x00 ,0x00,
   0x00 ,0x4f ,0x54 ,0x53 ,0xff ,0xfb ,0x90 ,0xc4 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00,
   0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x49 ,0x6e ,0x66 ,0x6f ,0x00 ,0x00 ,0x00 ,0x0f ,0x00,
   0x00 ,0x04 ,0x7e ,0x00 ,0x07 ,0x57 ,0x2e ,0x00 ,0x02 ,0x05 ,0x08 ,0x0a ,0x0d ,0x10 ,0x12 ,0x14 ,0x17,
   0x19 ,0x1c ,0x1f ,0x21 ,0x24 ,0x27 ,0x28 ,0x2b ,0x2e ,0x30 ,0x33 ,0x36 ,0x38 ,0x3b ,0x3d ,0x40 ,0x42,
   0x45 ,0x48 ,0x4a ,0x4d ,0x4f ,0x51 ,0x54 ,0x57 ,0x59 ,0x5c ,0x5f ,0x61 ,0x63 ,0x66 ,0x69 ,0x6b ,0x6e,
   0x71 ,0x73 ,0x76 ,0x78 ,0x7a ,0x7d ,0x80 ,0x82 ,0x85 ,0x88 ,0x8a ,0x8c ,0x8f ,0x92 ,0x94 ,0x97 ,0x9a,
   0x9c ,0x9e ,0xa1 ,0xa3
    };

   // Frame pattern example for various metadata for the media object
   uint8 pattern1[ID3V2_FRAME_HEADER_ID_LENGTH] = { 0x54, 0x49, 0x54, 0x32 };  //->"TIT1"
   uint8 pattern2[ID3V2_FRAME_HEADER_ID_LENGTH] = { 0x54, 0x50, 0x45, 0x31 };  //->"TIT2"
   uint8 pattern3[ID3V2_FRAME_HEADER_ID_LENGTH] = { 0x54, 0x41, 0x4c, 0x42 }; // -> "TALB"
   uint8 pattern4[ID3V2_FRAME_HEADER_ID_LENGTH] = { 0x54, 0x50, 0x45, 0x32 }; // -> "TPE2"
   uint8 pattern5[ID3V2_FRAME_HEADER_ID_LENGTH] = { 0x54, 0x50, 0x45, 0x32 }; // -> "TPE1"
*/

CurrentMediaObjIdFrame* ParseMediaObjectData(const uint8 *streamObject, uint32 streamLength, frameId frameType)
{
    const uint8 *streamTag = streamObject;
    bool hasId3V2Tag;
    uint32 headerCursor = 0;
    uint32 majorVersion;
    uint8 streamBeginning = 0;
    uint8 patternBeginning;
    CurrentMediaObjIdFrame *mediaFrameInfo = NULL;
    uint8 FramePattern[ID3V2_FRAME_HEADER_ID_LENGTH];
    bool validFrameType  = TRUE;

    if (streamLength == 0x00)
    {
        return NULL;
    }

     hasId3V2Tag = memcmp(streamObject, "ID3", ID3V2_TAG_HEADER_IDENTIFIER_LENGTH) == 0;

     /* Check for the id3v signature in the stream bytes */
     if (!hasId3V2Tag)
     {
         ID3V2_PARSER_INFO("parseMediaObjectData hasId3V2Tag %d\n", hasId3V2Tag);
         return NULL;
     }

     /* Every time header is read, stream curser will move ahead */
     headerCursor = currentCursor(&headerCursor, ID3V2_TAG_HEADER_IDENTIFIER_LENGTH);

     /* Get the majorVersion from the stream tag*/
     majorVersion = ((streamTag)[headerCursor+1] << 8) |
         ((streamTag)[headerCursor]);

     if (majorVersion != 3 && majorVersion != 4)
     {
         ID3V2_PARSER_INFO(" parseMediaObjectData majorVersion %d\n", majorVersion);
         /* No supported id3 tag found */
         return NULL;
     }

    switch (frameType)
    {
        case ID3V2_ALBUM_FRAME_ID:
        {
            memcpy(FramePattern, "TALB", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        case ID3V2_TITLE1_FRAME_ID:
        {
            memcpy(FramePattern, "TIT1", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        case ID3V2_TITLE2_FRAME_ID:
        {
            memcpy(FramePattern, "TIT1", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        case ID3V2_TITLE3_FRAME_ID:
        {
            memcpy(FramePattern, "TIT1", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        case ID3V2_ARTIST_FRAME_ID:
        {
            memcpy(FramePattern, "TPE1", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        case ID3V2_ALBUM_ARTIST_FRAME_ID:
        {
            memcpy(FramePattern, "TPE2", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        case ID3V2_TRACK_FRAME_ID:
        {
            memcpy(FramePattern, "TRCK", ID3V2_FRAME_HEADER_ID_LENGTH);
            break;
        }
        default:
        {
            ID3V2_PARSER_INFO("unknown frameId type: %d\n", frameType);
            validFrameType = FALSE;
            break;
        }
     }

     /* Application has sent invalid frameType */
     if (!validFrameType)
     {
         return NULL;
     }

     for (streamBeginning = 0; streamBeginning < streamLength - ID3V2_FRAME_HEADER_ID_LENGTH; ++streamBeginning)
     {
        for (patternBeginning = 0; patternBeginning < ID3V2_FRAME_HEADER_ID_LENGTH; ++patternBeginning)
        {
            if (streamTag[streamBeginning + patternBeginning] != FramePattern[patternBeginning])
            {
                break;
            }
        }
        if (patternBeginning == ID3V2_FRAME_HEADER_ID_LENGTH)
        {
            uint32 frameLength;
            uint32 cursor;
            uint32 frameData;
            uint8 frameHeader;
            uint8 skipNulBbytes = 0;

            ID3V2_PARSER_INFO("\n");

            for (frameHeader = streamBeginning; frameHeader < streamBeginning + ID3V2_FRAME_HEADER_ID_LENGTH; frameHeader++)
            {
                ID3V2_PARSER_INFO("%c", streamTag[frameHeader]);
            }

            cursor = streamBeginning;
            /* Move the cursor to get the frame header length */
            cursor = currentCursor(&cursor, ID3V2_FRAME_HEADER_ID_LENGTH);

            /* Get the frame length from the stream tag*/
            frameLength = ((streamTag)[cursor] << 24) |
                ((streamTag)[cursor + 1] << 16) |
                ((streamTag)[cursor + 2] << 8) |
                (streamTag)[cursor + 3];

            /* Move the cursor to get the frame data */
            cursor = currentCursor(&cursor, ID3V2_FRAME_HEADER_ID_LENGTH);

            /* check for stream is having encoding then need to skip those bytes */
            cursor = streamObjectHasBom(streamTag+cursor, &cursor);

            /* Frame may have null bytes which need to be skipped to get complete frame */
            while (streamTag[cursor] == 0x00 && skipNulBbytes < ID3V2_FRAME_HEADER_FLAGS_LENGTH)
            {
                skipNulBbytes++;
                cursor = cursor + 1;
            }

            mediaFrameInfo = (CurrentMediaObjIdFrame*)CsrPmemZalloc(sizeof(CurrentMediaObjIdFrame));

            if (mediaFrameInfo)
            {
                mediaFrameInfo->valueLength = frameLength;
                mediaFrameInfo->value = (uint8*)CsrPmemZalloc(sizeof(uint8)*frameLength);
                mediaFrameInfo->mediaframeType = frameType;
            }
            ID3V2_PARSER_INFO(":");
            for (frameData = 0; frameData < frameLength; frameData++)
            {
                if (mediaFrameInfo->value)
                {
                    mediaFrameInfo->value[frameData] = streamTag[frameData + cursor];
                }
                ID3V2_PARSER_INFO("%c", streamTag[frameData + cursor]);
            }
            ID3V2_PARSER_INFO("\n");
            return mediaFrameInfo;
        }
    }
    return NULL;
}
